import { BadRequest, InternalServerError } from 'http-errors'
import dayjs from 'dayjs'
import { EPayPageExecuteResFormat, ePaySDK, EPaySignalOrder } from '@/utils/pay'
import logger from '@/utils/logger'
import Order from '@/database/models/order'
import orderService from './orderService'
import userService from './userService'

/** 生成支付链接 */
const generatePay = async (userId: number, body: Body) => {
  const order = await orderService.getOrder(userId, body.outTradeNo, true)
  const { outTradeNo, totalAmount, subject } = order
  const bizContent = {
    type: body.type,
    out_trade_no: outTradeNo,
    money: totalAmount,
    name: subject,
  }
  const data = await ePaySDK.pageExecute(+body.resFormat || EPayPageExecuteResFormat.Url, {
    bizContent,
    return_url: process.env.EZF_RETURN_URL as string,
    notify_url: process.env.EZF_NOTIFY_URL as string,
  })
  if (data.code !== 200) {
    throw new InternalServerError(data.msg)
  }
  return data
}

/** 同步通知 */
const finishSync = async (query: Query) => {
  const verify = ePaySDK.checkNotifySign(query)
  if (!verify) {
    logger.warn('支付验签失败：', query)
    throw new BadRequest('支付验签失败。')
  }
  await paidSuccess(query)
}

/** 异步通知 */
const notifyAsync = async (query: Query) => {
  logger.info(`支付通知(异步):${query.type}-${query.name}`, query)
  const verify = ePaySDK.checkNotifySign(query)
  if (!verify) {
    logger.warn(`支付验签失败(异步):${query.type}-${query.name}`, query)
    throw new BadRequest('支付验签失败(异步)。')
  }
  await paidSuccess(query)
}

/** 主动查询ezf订单状态 */
const queryPayOrder = async (userId: number, outTradeNo: string) => {
  const order = await orderService.getOrder(userId, outTradeNo)
  const result = (await ePaySDK.queryOrder(order.outTradeNo)) as EPaySignalOrder
  if (result.code !== 1) {
    throw new BadRequest(result.msg)
  }
  return {
    userId: order.userId,
    outTradeNo: result.out_trade_no,
    tradeNo: result.trade_no,
    roleId: order.roleId,
    subject: result.name,
    durationMonths: order.durationMonths,
    status: +result.status,
    totalAmount: result.money,
    paymentMethod: result.type,
    createdAt: result.addtime,
    paidAt: result.endtime,
  }
}

/** 主动更新ezf订单状态 */
const updatePayOrder = async (userId: number, outTradeNo: string) => {
  const user = await userService.getUserInfo(userId)
  const order = await orderService.getOrder(userId, outTradeNo, true)
  const result = (await ePaySDK.queryOrder(order.outTradeNo)) as EPaySignalOrder
  if (result.code !== 1) {
    throw new BadRequest(result.msg)
  }
  order.set({
    tradeNo: result.trade_no,
    paymentMethod: result.type,
  })
  if (+result.status === 1) {
    order.set({
      status: 1,
      paidAt: dayjs(result.endtime || new Date()).toDate(),
    })
    // 如果过期时间大于当前时间，叠加有效期
    // 如果过期时间小于当前时间，以当前时间为过期时间，并叠加有效期
    const now = dayjs()
    const expiredAt = dayjs(user.expiredAt || now.toDate())
    user.set({
      roleId: order.roleId,
      expiredAt: now.isBefore(expiredAt)
        ? expiredAt.add(order.durationMonths, 'month').toDate()
        : now.add(order.durationMonths, 'month').toDate(),
    })
  } else if (dayjs().diff(dayjs(result.addtime), 'minute') > 3) {
    // 超过3分钟，订单超时，让order.status置为2
    order.status = 2
  }
  await order.save()
  await user.save()
}

/**
 * 支付成功后，更新订单状态和会员信息
 * @param {Query} query
 */
async function paidSuccess(query: Query) {
  const { out_trade_no, trade_no, type } = query
  const [order, ePayOrderInfo] = await Promise.all([
    Order.findOne({
      where: { outTradeNo: out_trade_no as string },
    }),
    ePaySDK.queryOrder(out_trade_no as string),
  ])
  if (!order || order.status > 0) {
    return
  }
  const endTime = (ePayOrderInfo as EPaySignalOrder).endtime
  await order.update({
    status: 1,
    tradeNo: trade_no as string,
    paymentMethod: type as string,
    paidAt: endTime ? dayjs(endTime).toDate() : dayjs().toDate(),
  })
  const user = await userService.getUserInfo(order.userId)
  user.roleId = order.roleId
  // 如果过期时间大于当前时间，叠加有效期
  // 如果过期时间小于当前时间，以当前时间为过期时间，并叠加有效期
  const now = dayjs()
  const expiredAt = dayjs(user.expiredAt || now.toDate())
  user.expiredAt = now.isBefore(expiredAt)
    ? expiredAt.add(order.durationMonths, 'month').toDate()
    : now.add(order.durationMonths, 'month').toDate()
  await user.save()
}

export default {
  generatePay,
  finishSync,
  notifyAsync,
  queryPayOrder,
  updatePayOrder,
}
